package com.hentie.tilelib;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.hentie.drawlib.IDraw;
import com.hentie.drawlib.PlaneMesh;
import com.hentie.drawlib.RenderQueue;
import com.hentie.utils.Base64;

import android.content.Context;
import android.content.res.AssetManager;

public class Level implements IDraw{

	Tileset tileSet;
	Layer[] tileLayers;

	boolean generatedMesh = false;
	// Create the level
	private Level(int levelWidth, int levelHeight) {
		// Create the three layers
		tileLayers = new Layer[3];
		// BAckground
		tileLayers[0] = new Layer(levelWidth, levelHeight, LayerType.BACKGROUND);
		// COLLISION
		tileLayers[1] = new Layer(levelWidth, levelHeight, LayerType.COLLIDABLE);
		// FORGROUND
		tileLayers[2] = new Layer(levelWidth, levelHeight, LayerType.FOREGROUND);
	}
	
	PlaneMesh[] drawMesh;

	//Use this to generate single meshes which are very dirty :3
	//Runs after tile generation
	private void GenerateDirtyMesh()
	{
		//Get how many tiles are valid in the are
		//int generateAmount = tileLayers[1].GetValidTileCount();
		int tileList[] = tileLayers[1].GetTileList();
		float tileWidth = tileSet.tileWidth;
		float tileHeight = tileSet.tileHeight;
		//Create the drawMesh
		drawMesh = new PlaneMesh[tileList.length];
		for(int i = 0; i < tileList.length; i++)
		{
			int currentTile = tileList[i];
			float[] textureInfo = tileSet.ReturnTexCoords(tileLayers[1].GetTileId(currentTile));
			drawMesh[i] = new PlaneMesh(1, 1);
			drawMesh[i].UpdatePlane(tileWidth/2.0f, tileHeight/2.0f, textureInfo[0], textureInfo[1], textureInfo[2], textureInfo[3]);
			drawMesh[i].position = tileLayers[1].GetPositionVector(currentTile,-500.0f);
			drawMesh[i].SetTextureName(tileSet.tileImage);
			drawMesh[i].useTextures = true;
		}
		generatedMesh = true;
	}
	 
	
	public static Level ParseLevelFromXML(Context context, String filename) throws DOMException, IOException {

		// The level file to return
		Level level = null;

		// Get assests
		AssetManager assets = context.getAssets();

		// Create XML Document
		Document dom = null;
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = null;
		try {
			db = factory.newDocumentBuilder();
		} catch (ParserConfigurationException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		InputStream is = null;
		try {
			is = assets.open(filename);
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}

		try {
			dom = db.parse(is);
		} catch (SAXException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// Access the attribute and get the level width and height
		int stringIndex = filename.lastIndexOf("/");
		if (stringIndex == -1)
			stringIndex = filename.length() - 1;
		// Get the asset directory
		String directory = filename.substring(0, stringIndex);

		// Okay now
		Element root = dom.getDocumentElement();
		int levelWidth = Integer.parseInt(root.getAttribute("width"));
		int levelHeight = Integer.parseInt(root.getAttribute("height"));
		// Create the level now
		level = new Level(levelWidth, levelHeight);
		// Now we construct the tileset
		// Now lets delve deep into the animations
		NodeList levelNodes = root.getChildNodes();

		for (int i = 0; i < levelNodes.getLength(); i++) {
			Node currentNode = levelNodes.item(i);
			if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
				Element nodeElem = (Element) currentNode;
				if (nodeElem.getTagName().equals("tileset")) {

					// Get the tile set information
					String tileSetName = nodeElem.getAttribute("name");
					int tileSetWidth = Integer.parseInt(nodeElem
							.getAttribute("tilewidth"));
					int tileSetHeight = Integer.parseInt(nodeElem
							.getAttribute("tileheight"));
					
					NodeList imageNodeList = nodeElem.getElementsByTagName("image");
					
					// Get the child element
					Node imageNode = imageNodeList.item(0);

					Element imageElem = (Element)imageNode;
					
					if (imageElem != null) {
						String imageName = imageElem.getAttribute("source");
						int imagePos = imageName
								.lastIndexOf("/");
						if(imagePos!=-1)
						imageName = imageName.substring(imagePos-1);
						int imageWidth = Integer.parseInt(imageElem
								.getAttribute("width"));
						int imageHeight = Integer.parseInt(imageElem
								.getAttribute("height"));
						// Create the tileSet
						level.tileSet = new Tileset(tileSetName, directory
								+"/"+ imageName, tileSetWidth, tileSetHeight,
								imageWidth, imageHeight);
					} else
						throw new NullPointerException();

				} else if (nodeElem.getTagName().equals("layer")) {
					// Get the child data element
					Element dataElem = (Element) nodeElem.getElementsByTagName("data").item(0);
					// get other attributes
					String encoding = dataElem.getAttribute("encoding");
					String compress = dataElem.getAttribute("compression");
					String zipString = dataElem.getChildNodes().item(0).getNodeValue();
					NodeList tileNodes = dataElem.getChildNodes(); //Get the tile information
					if(encoding == null)
						level.tileLayers[1].tiles = FromNodes(tileNodes);
					else
						level.tileLayers[1].tiles = DecompressTiles(level.tileSet,level.tileLayers[1].layerWidth,level.tileLayers[1].layerHeight,encoding,compress,zipString);
					
					
					

				}

			}
		}
		
		level.GenerateDirtyMesh();

		// Return te level
		return level;
	}
	
	
	public static Tile[] FromNodes(NodeList nodes)
	{
		
		
		
		return null;
	}
	
	
	public static Tile[] DecompressTiles(Tileset set,int width,int height,String encoding,String compress, String compressString) throws IOException
	{
		int totalTile = width*height;

		Tile[] tiles = new Tile[totalTile];
		
		char[] charArray = compressString.trim().toCharArray();
		byte[] byteArray = Base64.decode(charArray);
		//We have the byte data now we decompress
		//Create the inputStream
		ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
		InputStream is= null;
		if("zlib".equals(compress))
			is= new InflaterInputStream(bais);
		
		
		for(int i = 0; i < totalTile; i++)
		{
			//Initialise the tile
			tiles[i]= new Tile();
			//The default tile
                 int tileId = 0;
                 //Read the stream
                 tileId |= is.read();
                 tileId |= is.read() <<  8;
                 tileId |= is.read() << 16;
                 tileId |= is.read() << 24;
        		
                 tiles[i].tileID = tileId-1;
        			//Calculat the width and height position
        			
        			
        			tiles[i].gridX = i%width;
        			tiles[i].gridY = i/width;
        			tiles[i].tileWidth = set.tileWidth;
        			tiles[i].tileHeight = set.tileHeight;
        		}
                 
		
		return tiles;
		
	}


	public void Draw(RenderQueue queue, float deltaTime) {
		
		if(generatedMesh)
		// TODO Auto-generated method stub
		for(int i =0; i < drawMesh.length;i++)
		{
			queue.AddToRenderQueue(drawMesh[i]);
		}
		
	}
}
